package com.hps.demo.netty.server;

import com.hps.demo.annotation.RpcService;
import com.hps.demo.netty.codec.JSONDecoder;
import com.hps.demo.netty.codec.JSONEncoder;
import com.hps.demo.registry.ServiceRegistry;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import java.util.HashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @author heps
 * @since 2020/8/15
 */
@Slf4j
@Component
public class NettyServer implements ApplicationContextAware, InitializingBean {

  private static final EventLoopGroup bossGroup = new NioEventLoopGroup(1);

  private static final EventLoopGroup workerGroup = new NioEventLoopGroup(4);

  private Map<String, Object> serviceMap = new HashMap<>();

  @Value("${rpc.server.address}")
  private String serverAddress;

  @Autowired
  private ServiceRegistry serviceRegistry;

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    Map<String, Object> beans = applicationContext
        .getBeansWithAnnotation(RpcService.class);
    for (Object serviceBean : beans.values()) {
      Class<?> clazz = serviceBean.getClass();
      Class<?>[] interfaces = clazz.getInterfaces();
      for (Class<?> inter : interfaces) {
        String interfaceName = inter.getName();
        log.info("加载服务类: {}", interfaceName);
        serviceMap.put(interfaceName, serviceBean);
      }
    }
    log.info("已加载全部服务接口: {}", serviceMap);
  }

  @Override
  public void afterPropertiesSet() throws Exception {
    start();
  }

  public void start() {
    final NettyServerHandler handler = new NettyServerHandler(serviceMap);
    new Thread(() -> {
      try {
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup)
            .channel(NioServerSocketChannel.class)
            .option(ChannelOption.SO_BACKLOG, 1024)
            .childOption(ChannelOption.SO_KEEPALIVE, true)
            .childOption(ChannelOption.TCP_NODELAY, true)
            .childHandler(new ChannelInitializer<SocketChannel>() {

              @Override
              protected void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline()
                    .addLast(new IdleStateHandler(0, 0, 60))
                    .addLast("encoder", new JSONEncoder())
                    .addLast("decoder", new JSONDecoder())
                    .addLast(handler);
              }
            });
        String[] array = serverAddress.split(":");
        String host = array[0];
        int port = Integer.parseInt(array[1]);
        ChannelFuture cf = bootstrap.bind(host, port).sync();
        log.info("RPC 服务器启动, 监听端口: {}", port);
        serviceRegistry.register(serverAddress);
        // 等待服务端监听端口关闭
        cf.channel().closeFuture().sync();
      } catch (Exception e) {
        e.printStackTrace();
      } finally {
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
      }
    }).start();
  }
}
